/*
 * Author: 李波（Edwin Lee)
 * Date: 2024-06-15 13:08:27
 * FilePath: /chat/src/server/chatservice.cc
 * LastEditTime: 2024-07-12 11:40:03
 * version: 1.0
 * Description:
 */

#include "public.h"
#include "chatservice.h"
#include "offlinemessagemodel.h"

#include <muduo/base/Logging.h>
#include <vector>
#include <map>
using namespace std;
using namespace muduo;

ChatService *ChatService::getInstance()
{
    static ChatService service;
    return &service;
}

ChatService::ChatService()
{
    msgHandlerMap_.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
    msgHandlerMap_.insert({LOGOUT_MSG, std::bind(&ChatService::logout, this, _1, _2, _3)});
    msgHandlerMap_.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
    msgHandlerMap_.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1,_2,_3)});
    msgHandlerMap_.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1,_2,_3)});

    msgHandlerMap_.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1,_2,_3)});
    msgHandlerMap_.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1,_2,_3)});
    msgHandlerMap_.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1,_2,_3)});

    // 连接redis服务器
    if(redis_.connect())
    {
        // 设置上报信息的回调
        redis_.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, _1, _2));
    }
}

void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    LOG_INFO << "do login service!";
    int id = js["id"];
    string pwd = js["password"];

    User user = userModel_.query(id);
    if (user.getId() == id && user.getPwd() == pwd)
    {
        if (user.getState() == "online")
        {
            // 该用户已经登录，不允许重复登录
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "该用户已经登录，请重新输入新账号";
            conn->send(response.dump());
        }
        else
        {
            //记录用户连接信息
            {
                lock_guard<mutex> lock(connMutex_);
                userConnMap_.insert({id,conn});
            }
            // id用户登录成功后，向redis订阅channel（id）
            redis_.subscribe(id);
            //登录成功，更新用户状态信息，state offline => online
            user.setState("online");
            LOG_INFO << js.dump();
            userModel_.updateState(user);
            LOG_INFO << "login success!";
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["id"] = user.getId();
            response["name"] = user.getName();
            //查询该用户是否有离线消息

            vector<string> vstr = offlineMsgModel_.query(id);
            if(!vstr.empty())
            {
                response["offlinemsg"] = vstr;
                //读取该用户的离线消息后，把该用户的消息删除
                offlineMsgModel_.remove(id);
            }
            // 查询该用户的好友信息
            vector<User> vuser = friendModel_.query(id);
            if(!vuser.empty())
            {
                vector<string> vec2;
                for(User& user:vuser)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec2.push_back(js.dump());
                }
                response["friends"] = vec2;
            }

            conn->send(response.dump().c_str());
        }
    }
    else
    {
        LOG_INFO << "login failed!";
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["errmsg"] = "用户名或密码不正确";
        conn->send(response.dump());
    }
}

void ChatService::logout(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"];
    {
        lock_guard<mutex> lock(connMutex_);
        auto it = userConnMap_.find(userid);
        if (it != userConnMap_.end())
        {
            userConnMap_.erase(it);
        }
    }
    // 用户注销，相当于就是下线，在redis中取消订阅通道
    redis_.unsubscribe(userid); 
    // 更新用户的状态信息
    User user(userid, "", "", "offline");
    userModel_.updateState(user);
}

void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    LOG_INFO << "do reg service!";
    string name = js["name"];
    string pwd = js["password"];

    User user;
    user.setName(name);
    user.setPwd(pwd);
    bool state = userModel_.insert(user);
    if (state)
    {
        // 注册成功
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.getId();
        conn->send(response.dump());
        LOG_INFO << "注册成功";
    }
    else
    {
        // 注册失败
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;
        conn->send(response.dump());
        LOG_INFO << "注册失败";
    }
}

MsgHandler ChatService::getHandler(int msgid)
{
    // 记录错误日志，msgid没有对应的事件处理回调
    auto it = msgHandlerMap_.find(msgid);
    if (it == msgHandlerMap_.end())
    {
        // 返回一个默认的空处理器
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp timd)
        {
            // muduo库的错误输出不需要endl结束符号
            LOG_ERROR << "msgid:" << msgid << " can not find handler!";
        };
    }
    return msgHandlerMap_[msgid];
}

void ChatService::clientCloseException(const TcpConnectionPtr& conn)
{
    User user;
    {
        lock_guard<mutex> lock(connMutex_);
        for(auto it = userConnMap_.begin(); it != userConnMap_.end(); ++it)
        {
            if(it->second == conn)
            {
                //从map表删除用户的链接信息
                user.setId(it->first);
                userConnMap_.erase(it);
                break;
            }
        }
    }
    // 注销订阅通道
    redis_.unsubscribe(user.getId());
    //更新用户的状态信息
    if(user.getId() != -1)
    {
        user.setState("offline");
        userModel_.updateState(user);
    }
}

void ChatService::oneChat(const TcpConnectionPtr& conn, json& js, Timestamp time)
{
    int toid = js["toid"];
    bool userState = false;
    {
        lock_guard<mutex> lock(connMutex_);
        auto it = userConnMap_.find(toid);
        if(it != userConnMap_.end())
        {
            // toid在线，转发消息 服务器主动推送消息给toid用户
            // UNDO：此处后期可以开发信息存储
            it->second->send(js.dump());
            return;
        }
    }
    // 查看toid是否在线
    User user = userModel_.query(toid);
    LOG_INFO << "publish begin 666";
    if(user.getState() == "online")
    {
        redis_.publish(toid, js.dump());
        LOG_INFO << "publish finish toid=" << toid << " jsdump=" <<js.dump();
        return;
    }
    // toid不在线，存储离线消息
    offlineMsgModel_.insert(toid,js.dump());
}

void ChatService::reset()
{
    userModel_.resetState();
}

void ChatService::addFriend(const TcpConnectionPtr& conn, json& js, Timestamp time)
{
    int userid = js["id"];
    int friendid = js["friendid"];
    
    //存储好友信息
    friendModel_.insert(userid,friendid);
    LOG_INFO << "id:" << userid << " friendid:" << friendid;
}

void ChatService::createGroup(const TcpConnectionPtr& conn, json& js, Timestamp time)
{
    int userid = js["id"];
    string name = js["groupname"];
    string desc = js["groupdesc"];

    Group group(-1,name,desc);
    if(groupModel_.createGroup(group))
    {
        groupModel_.addGroup(userid, group.getId(), "creator");
    }
}

void ChatService::addGroup(const TcpConnectionPtr& conn, json& js, Timestamp time)
{
    int userid = js["id"];
    int groupid = js["groupid"];
    groupModel_.addGroup(userid, groupid, "normal");
}

void ChatService::groupChat(const TcpConnectionPtr& conn, json& js, Timestamp time)
{
    int userid = js["id"];
    int groupid = js["groupid"];
    vector<int> vuserid = groupModel_.queryGroupUsers(userid, groupid);

    lock_guard<mutex> lock(connMutex_);
    for(int id:vuserid)
    {
        auto it = userConnMap_.find(id);
        if(it != userConnMap_.end())
        {
            //转发群消息
            it->second->send(js.dump());
        }
        else
        {
            //查询toid是否在线
            User user = userModel_.query(id);
            if(user.getState() == "online")
            {
                redis_.publish(id, js.dump());
            }
            else
            {
                //存储离线群消息
                offlineMsgModel_.insert(id,js.dump());
            }
        }
    }
}

void ChatService::handleRedisSubscribeMessage(int userid, string msg)
{
    lock_guard<mutex> lock(connMutex_);
    auto it = userConnMap_.find(userid);
    if(it != userConnMap_.end())
    {
        it->second->send(msg);
        return;
    }
    offlineMsgModel_.insert(userid, msg);
}